#include "PrecompiledHeaderRenderer_OpenGL.h"
#include "VertexBufferWrapper.h"

/*--------------------------------------------------------------------------*/
GLVertexBuffer* VertexBufferWrapper::_boundArrayBuffer = NULL;
GLVertexBuffer* VertexBufferWrapper::_boundElementArrayBuffer = NULL;
int VertexBufferWrapper::_vertexBuffersCount = 0;
std::map<std::string, GLVertexBuffer*> VertexBufferWrapper::_vertexBuffersMap;
/*--------------------------------------------------------------------------*/

const VertexDeclaration g_vertexDeclarations[] =
{
	{	EVertexDeclaration::Position0,			0,	3,	1,	GL_FLOAT,	12,	false,	"vec3"	},
	{	EVertexDeclaration::Position1,			1,	3,	1,	GL_FLOAT,	12,	false,	"vec3"	},
	{	EVertexDeclaration::Normal0,			2,	3,	1,	GL_FLOAT,	12,	false,	"vec3"	},
	{	EVertexDeclaration::Normal1,			3,	3,	1,	GL_FLOAT,	12,	false,	"vec3"	},
	{	EVertexDeclaration::Texcoord0,			4,	2,	1,	GL_FLOAT,	8,	false,	"vec2"	},
	{	EVertexDeclaration::Texcoord1,			5,	2,	1,	GL_FLOAT,	8,	false,	"vec2"	},
	{	EVertexDeclaration::Texcoord2,			6,	2,	1,	GL_FLOAT,	8,	false,	"vec2"	},
	{	EVertexDeclaration::Texcoord3,			7,	2,	1,	GL_FLOAT,	8,	false,	"vec2"	},
	{	EVertexDeclaration::Color0,				8,	4,	1,	GL_FLOAT,	4,	false,	"vec4"	},
	{	EVertexDeclaration::Color1,				9,	4,	1,	GL_FLOAT,	4,	false,	"vec4"	},

	{	EVertexDeclaration::Instancing,			10,	4,	4,	GL_FLOAT,	64,	true,	"mat4"	},

	{	EVertexDeclaration::FontData,			10,	4,	3,	GL_FLOAT,	48,	true,	""	},

	{	EVertexDeclaration::BasicVertex,		10,	4,	3,	GL_FLOAT,	48,	false,	""	},

	{	EVertexDeclaration::VoxelType,			10,	1,	1,	GL_UNSIGNED_INT,	4,	false, "uint"	},

	{	EVertexDeclaration::Indices,			31,	0,	0,	GL_UNSIGNED_INT,	4,	false, ""	},
	{	EVertexDeclaration::Unknown,			31,	0,	0,	GL_FLOAT,	0,	false,	""	},

};

/*--------------------------------------------------------------------------*/

VertexBufferWrapper::VertexBufferWrapper()
{
}

/*--------------------------------------------------------------------------*/

VertexBufferWrapper::~VertexBufferWrapper()
{
	for (auto i = _vertexBuffersMap.begin(); i != _vertexBuffersMap.end(); ++i)
	{
		AssertLogE(true, "VertexBuffer %s not deleted!", i->first.c_str());
	}

	AssertLogC(_vertexBuffersMap.size() != 0, "Some VertexBuffers left undeleted!");
}

/*--------------------------------------------------------------------------*/

GLVertexBuffer* VertexBufferWrapper::GenBuffer(const std::string& name, GLenum type, uint sizeBytes, GLenum access)
{
	AssertLogC(name == "", "Zero length name");
	AssertLogC(_vertexBuffersMap[name] != NULL, "VertexBuffer %s already exists!", name.c_str());

	GLVertexBuffer* vb = NULL;

	switch (type)
	{
	case GL_ARRAY_BUFFER:
	{
		_boundArrayBuffer = NULL;
		vb = new GLVertexBuffer(name, type, sizeBytes, access);
		break;
	}

	case GL_ELEMENT_ARRAY_BUFFER:
	{
		_boundElementArrayBuffer = NULL;
		vb = new GLVertexBuffer(name, type, sizeBytes, access);
		break;
	}

	default:
	{
		AssertLogC(true, "Unknown VertexBuffer type");
	}
	}

	AssertLogC(vb == NULL, "Cannot create VertexBuffer")

	glGenBuffers(1, &vb->handle);
	AssertLogC(vb->handle == 0, "Failed to create texture");

	BindBuffer(vb, true);
	glBufferData(type, sizeBytes, NULL, access);

	vb->shadow = new uchar [sizeBytes];
	memset(vb->shadow, 0, sizeBytes);

	vb->semantic = EVertexDeclaration::Unknown;

	_vertexBuffersMap[name] = vb;
	_vertexBuffersCount += 1;
	return vb;
}

/*--------------------------------------------------------------------------*/

GLVertexBuffer* VertexBufferWrapper::GenBuffer(const std::string& name, EVertexDeclaration::Type semantic, uint numVertices, GLenum access)
{
	GLVertexBuffer* vb = NULL;

	switch (semantic)
	{
	case EVertexDeclaration::Indices:
	{
		vb = GenBuffer(name, GL_ELEMENT_ARRAY_BUFFER, numVertices * sizeof(uint), access);
		vb->semantic = semantic;
		break;
	}

	default:
	{
		vb = GenBuffer(name, GL_ARRAY_BUFFER, numVertices * g_vertexDeclarations[(uint) semantic].m_size, access);
		vb->semantic = semantic;
	}
	}

	return vb;
}

/*--------------------------------------------------------------------------*/

void VertexBufferWrapper::DeleteBuffer(GLVertexBuffer** vb)
{
	if (vb && *vb)
	{
		auto i = _vertexBuffersMap.find((*vb)->name);
		AssertLogC(i == _vertexBuffersMap.end(), "VertexArray %s not found in map!", (*vb)->name.c_str());
		_vertexBuffersMap.erase(i);

		/*--------------------------------------------------------------------------*/

		_vertexBuffersCount -= 1;

		/*--------------------------------------------------------------------------*/

		glDeleteBuffers(1, &(*vb)->handle);
		delete [](*vb)->shadow;
		(*vb)->shadow = NULL;

		delete *vb;
		*vb = NULL;
	}
}

/*--------------------------------------------------------------------------*/

void* VertexBufferWrapper::MapBuffer(GLVertexBuffer* vb)
{
	AssertLogC(vb == NULL, "Cannot map NULL buffer");
	AssertLogC(vb->handle == 0, "Cannot map 0 buffer");
	AssertLogC(vb->mapped == true, "Cannot map busy buffer");

	if (vb && vb->mapped == false)
	{
		vb->mapped = true;
		BindBuffer(vb, true);
		void* ptn = glMapBuffer(vb->type, GL_READ_WRITE);

		AssertLogC(ptn == NULL, "Failed to map VertexBuffer (1)!");

		return ptn;
	}
	else
	{
		AssertLogC(true, "Failed to map VertexBuffer (2)!");
		return NULL;
	}
}

/*--------------------------------------------------------------------------*/

void VertexBufferWrapper::UnmapBuffer(GLVertexBuffer* vb)
{
	AssertLogC(vb == NULL, "Cannot unmap NULL buffer");
	AssertLogC(vb->handle == 0, "Cannot unmap 0 buffer");
	AssertLogC(vb->mapped == false, "Cannot unmap busy buffer");

	if (vb && vb->mapped == true)
	{
		glUnmapBuffer(vb->type);
		vb->mapped = false;
	}
}

/*--------------------------------------------------------------------------*/

void VertexBufferWrapper::BindBuffer(GLVertexBuffer* vb, bool force)
{
	if (vb)
	{
		switch (vb->type)
		{
		case GL_ARRAY_BUFFER:
		{
			if (force || _boundArrayBuffer != vb)
			{
				_boundArrayBuffer = vb;
				glBindBuffer(GL_ARRAY_BUFFER, vb->handle);
			}

			break;
		}

		case GL_ELEMENT_ARRAY_BUFFER:
		{
			if (force || _boundElementArrayBuffer != vb)
			{
				_boundElementArrayBuffer = vb;
				glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vb->handle);
			}

			break;
		}

		default:
		{
			AssertLogC(true, "Unknown VertexBuffer type");
		}
		}
	}
}

/*--------------------------------------------------------------------------*/

void VertexBufferWrapper::CopyDataToShadow(GLVertexBuffer* vb, void* sdata, uint size, uint offset)
{
	if (vb)
	{
		AssertLogC(offset + size > vb->size, "Buffer %s overflow", vb->name);

		memcpy((uchar*) vb->shadow + offset, sdata, size);
		vb->updateBeginByte = std::min(vb->updateBeginByte, offset);
		vb->updateEndByte = std::max(vb->updateEndByte, offset + size);
	}
}

/*--------------------------------------------------------------------------*/

void VertexBufferWrapper::FlushShadowBuffer(GLVertexBuffer* vb)
{
	if (vb)
	{
		if (vb->updateEndByte	!= 0)
		{
			BindBuffer(vb);
			glBufferSubData(vb->type, vb->updateBeginByte, vb->updateEndByte - vb->updateBeginByte, ((uchar*) vb->shadow) + vb->updateBeginByte);

			vb->updateEndByte = 0;
			vb->updateBeginByte = vb->size;
		}
	}
}

/*--------------------------------------------------------------------------*/

void VertexBufferWrapper::UnbindBuffers()
{
	_boundArrayBuffer = NULL;
	glBindBuffer(GL_ARRAY_BUFFER, 0);

	_boundElementArrayBuffer = NULL;
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}

/*--------------------------------------------------------------------------*/

GLVertexBuffer* VertexBufferWrapper::GetBuffer(const std::string& name)
{
	auto iter = _vertexBuffersMap.find(name);

	if (iter == _vertexBuffersMap.end())
	{
		return NULL;
	}
	else
	{
		return iter->second;
	}
}

/*--------------------------------------------------------------------------*/

void* VertexBufferWrapper::GetShadowBuffer(GLVertexBuffer* vb, uint sdata, uint offset)
{
	if (vb)
	{
		vb->updateBeginByte = std::min(vb->updateBeginByte, offset);
		vb->updateEndByte = std::max(vb->updateEndByte, sdata + offset);

		return (void*)((uint) vb->shadow + offset);
	}

	return NULL;
}

/*--------------------------------------------------------------------------*/

void* VertexBufferWrapper::GetShadowBuffer(GLVertexBuffer* vb)
{
	if (vb)
	{
		vb->updateBeginByte = 0;
		vb->updateEndByte = vb->size;

		return (void*) vb->shadow;
	}

	return NULL;
}

/*--------------------------------------------------------------------------*/

int VertexBufferWrapper::GetSemanticLocation(const std::string& semantic)
{
	EVertexDeclaration::Type type = EVertexDeclaration::FromStr(semantic.c_str());

	return g_vertexDeclarations[type].m_location;
}

/*--------------------------------------------------------------------------*/

std::string VertexBufferWrapper::GetSemanticType(const std::string& semantic)
{
	EVertexDeclaration::Type type = EVertexDeclaration::FromStr(semantic.c_str());

	return g_vertexDeclarations[type].m_glslType;
}

/*--------------------------------------------------------------------------*/
